home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
System Booster
/
System Booster.iso
/
Archives
/
Timing
/
clock1_2.lha
/
clock1_2
/
clock.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-05-16
|
40KB
|
1,236 lines
/*****************************************************************
* clock - A flexible, efficient digital clock with date and memory watching
*
* Copyright 1994 by Peter Schachte
*
* This is an effecient titlebar digital clock, with date, day of week,
* free chip/fast/total memory, largest free chip/fast/either memory
* block, control over formatting, pen color for various parts of the
* display, and refresh frequency. The clock can also show the local
* time in a different time zone. Clock only updates parts of the
* display that need updating, so CPU usage is kept low. This program
* should work under AmigaOS 1.2 and up.
*
* This code is based on Mike Meyer's clock program, though by now
* very little of his code remains. In fact, I've changed the
* spirit of his program, which was quite small and simple; now
* it's (relatively) large and complex, and probably has altogether
* too many features.
*
* Nevertheless, here's Mike's original copyright notice:
*================================================================
* clock - a dumb, digital clock in the upper right-hand corner. Designed
* to be small, not flexible!
*
* Copyright (c) 1986, Mike Meyer
*
* Permission is hereby granted to distribute this program for any purposes
* whatsoever, so long as this notice, including the above copyright, is
* included with the distribution. Unlike other people, I don't care if you
* make money off of this, so long as I get credit for having written it.
*================================================================
*
* I likewise hereby grant the right to distribute this program for
* any purposes whatsoever, as long as this notice, including my
* copyright and Mike's, is included with the distribution.
*
*****************************************************************/
static char version[] =
"$VER: " PROGNAME " " VERSION " (" __DATE__ ") © 1994 Peter Schachte";
/* There is one feature that I have #ifdef'ed out of this program. If
* CORRECTION is defined when this file is processed, this clock will
* support the extra command line argument and tooltype "CORRECTION"
* which should be set to the number of seconds until your system
* clock gains or loses a second, positive if it loses a second or
* negative if it gains a second. If this option is specified, then
* the clock will automatically correct the system time as you
* specify.
*
* This feature is only useful if your system clock gains or loses
* time consistently. Mine doesn't, and I assume the same is true of
* others, so I've left this feature out by default. But I may change
* my mind and put it back sometime.
*/
#include "progargs.h"
#include <stdlib.h>
#include <string.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/tasks.h>
#include <devices/timer.h>
#include <libraries/dos.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <graphics/rastport.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/exec.h>
#include <proto/graphics.h>
/* In case we're linked with cback.o */
char *__procname = PROGNAME;
long __BackGroundIO = 0;
/*****************************************************************
Various Useful Constants
*****************************************************************/
#define MINUTES_PER_DAY 1440
#define SECS_PER_DAY 86400
#define END_OF_TIME ((unsigned long) -1)
/* (approx?) sizes of workbench gadgets for OS >=2.0 and OS <=1.3 */
#define WIDTH_OF_20_CLOSE_GADGET 20
#define WIDTH_OF_20_DEPTH_GADGET 23
#define WIDTH_OF_13_CLOSE_GADGET 27
#define WIDTH_OF_13_DEPTH_GADGET 27
#define INTUITION_REV 1L
#define DEFAULT_FORMAT "%2Chip: %1%G%|%2Fast: %1%F%|%e %b %y%|%A%|%q:%M"
/* An individual part of the display may need any of the following
* data in order to be displayed. Some parts may need multiple parts,
* so we use bit masks to represent them.
*/
#define NEED_CHIP 0
#define NEED_FAST 1
#define NEED_BIG_CHIP 2
#define NEED_BIG_FAST 3
#define NEED_TIME 4
#define MAX_NEED 4
#define NO_NEEDS_MASK 0
#define NEED_CHIP_MASK (1<<NEED_CHIP)
#define NEED_FAST_MASK (1<<NEED_FAST)
#define NEED_MEM_MASK (NEED_CHIP_MASK|NEED_FAST_MASK)
#define NEED_BIG_CHIP_MASK (1<<NEED_BIG_CHIP)
#define NEED_BIG_FAST_MASK (1<<NEED_BIG_FAST)
#define NEED_BIG_MEM_MASK (NEED_BIG_CHIP_MASK|NEED_BIG_FAST_MASK)
#define NEED_TIME_MASK (1<<NEED_TIME)
/*****************************************************************
Maintaining The Window
We use a lazy redisplay technology that only paints (about) as much of
the window as needs to be displayed. To do this, we maintain a chain
of display_element structures, each representing one part of the
display. In addition, we also maintain two separate chains of the
same structures, one for the time- and date-related fields
(time_chain) and one for the available memory fields (mem_chain).
When we wish to update the display, we traverse the time_chain and
mem_chain, updating only those fields. When the whole window needs to
be updated, we traverse the whole chain drawing all strings and bars.
We are also a bit lazy with respect to updates: each display_element
contains the value currently displayed in the window, and if we find
that field hasn't changed, we don't do any drawing. For time/date
fields we take this one step further. We organize the time_chain in
increasing order of "size" of the units (first seconds, then minutes,
etc.). When we find an element that hasn't changed, we know that the
following elements won't have changed, either, and so we quit looking.
To make this work, we store the previous value in units since 1 Jan
1976; e.g., the minute will be stored as minutes since midnight, 1 Jan
1976. The minute shown in the clock is just that number modulo 60.
One further thing: we want the display to look nice in the presence of
alphabetic month and weekday displays, which vary quite a bit in
length. Of course, we don't want to resize the window every time the
day changes, and we don't even want the individual segments of the
display, separated by vertical bars, to change in size (the bars
should stay put). This means that when the day changes, we have to
replan the display. When we start up, we plan the display assuming
the greatest possible space needed by each field. Then, each time the
day changes, we replan using the actual sizes of the fields, and
centering each display segment between the (unmoving) bars (or the
ends of the display).
*****************************************************************/
/* these are the various kinds of data that can be displayed */
enum display_kind {
invalid, space, vbar, string,
chip_mem, chip_mem_K, fast_mem, fast_mem_K, total_mem, total_mem_K,
largest_chip, largest_chip_K, largest_fast, largest_fast_K,
largest_mem, largest_mem_K,
/* NB: this group is carefully ordered so that if one of these
* hasn't changed since the last update, then later ones haven't either
*/
second, minute, hour12, hour12_0, hour24, hour24_0, am_pm,
/* this group is similarly ordered */
julian, day, day0, week_num_sun, week_num_mon, weekday_abbrev,
weekday_full, weekday_num, monthnum0, monthnum, month_abbrev,
month_full, year, year100
};
/* for each part of the display, we have one of these */
struct display_element {
struct display_element *next; /* all elements in order */
struct display_element *next_of_kind; /* elements of similar kind */
short left; /* pixel left of element */
short max_width; /* max width of element */
short curr_width; /* current width of elt */
short pen; /* pen to draw in */
enum display_kind kind; /* what is to be shown */
union {
struct string { /* for strings: */
char *text; /* the string to show */
int length; /* char length of string */
} string;
struct data { /* for most other kinds: */
int curr_value; /* value currently shown */
} data;
};
};
/*****************************************************************
Command Line Args and Icon Tooltypes
*****************************************************************/
int background_pen = 0; /* default background is pen 0 */
char *format_string = DEFAULT_FORMAT;
int interval_tenth_secs = 10; /* default to one update per second */
int offset_minutes = 0; /* default to no time zone offset */
int priority = 1; /* default priority */
int win_left; /* default left: relative to screen */
/* width and window width. Don't */
/* know default till we know OS ver */
int win_top = 0; /* default top */
int horiz_padding = 5; /* pixels of padding on either side */
/* of clock */
#ifdef CORRECTION
int correction_ratio = 0; /* add one second to the time */
/* every correction_ratio */
/* seconds; subtract if negative */
#endif
struct arg_descriptor arg_desc[] = {
INT_ARG("BACKGROUND", background_pen),
STRING_ARG("FORMAT", format_string),
INT_ARG("INTERVAL", interval_tenth_secs),
INT_ARG("OFFSET", offset_minutes),
INT_ARG("PRIORITY", priority),
INT_ARG("LEFT", win_left),
INT_ARG("TOP", win_top),
INT_ARG("PADDING", horiz_padding),
#ifdef CORRECTION
INT_ARG("CORRECTION", correction_ratio)
#endif
};
/*****************************************************************
Constants
*****************************************************************/
static struct NewWindow New_Window = {
0, 0, 0, 0, /* Fill these in later */
255, 255, /* Default pens */
IDCMP_ACTIVEWINDOW | IDCMP_INACTIVEWINDOW |
IDCMP_CHANGEWINDOW |
IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW,
WINDOWCLOSE | WINDOWDRAG | NOCAREREFRESH,
(struct Gadget *) NULL,
(struct Image *) NULL,
(UBYTE *) NULL, /* No title */
(struct Screen *) NULL,
(struct BitMap *) NULL,
0, 0, 0, 0,
WBENCHSCREEN
};
static char *str_am_pm[2] = {"AM", "PM"};
static char *str_weekday[7] = {"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"};
static char *str_month[12] = {"January", "February", "March", "April", "May",
"June", "July", "August", "September",
"October", "November", "December"};
/* I'm using tables for quite a few things here to keep the code
* small. It's fairly clean, too, in most places. In just about all
* of these cases, there are exceptions and special cases that are
* treated separately in the code.
*/
/* display_kind for each upper case format letter */
static char upper_codes[26] = {
/* A */ (char) weekday_full,
/* B */ (char) month_full,
/* C */ (char) invalid, /* short for "%a %b %e %T %Z %Y" */
/* D */ (char) invalid, /* short for "%m/%d/%y" */
/* E */ (char) invalid,
/* F */ (char) fast_mem_K,
/* G */ (char) chip_mem_K,
/* H */ (char) hour24_0,
/* I */ (char) hour12_0,
/* J */ (char) monthnum,
/* K */ (char) total_mem,
/* L */ (char) invalid,
/* M */ (char) minute,
/* N */ (char) invalid,
/* O */ (char) largest_chip_K,
/* P */ (char) invalid,
/* Q */ (char) hour12,
/* R */ (char) invalid, /* short for "%H:%M" */
/* S */ (char) second,
/* T */ (char) invalid, /* short for "%H:%M:%S" */
/* U */ (char) week_num_sun,
/* V */ (char) largest_fast_K,
/* W */ (char) week_num_mon,
/* X */ (char) invalid, /* short for "%H:%M:%S" */
/* Y */ (char) year,
/* Z */ (char) largest_mem_K,
};
/* display_kind for each lower case format letter */
static char lower_codes[26] = {
/* a */ (char) weekday_abbrev,
/* b */ (char) month_abbrev,
/* c */ (char) invalid, /* short for "%a %b %d %H:%M:%S %Y" */
/* d */ (char) day0,
/* e */ (char) day,
/* f */ (char) fast_mem,
/* g */ (char) chip_mem,
/* h */ (char) month_abbrev,
/* i */ (char) invalid,
/* j */ (char) julian,
/* k */ (char) total_mem_K,
/* l */ (char) invalid,
/* m */ (char) monthnum0,
/* n */ (char) invalid, /* ignore newlines */
/* o */ (char) largest_chip,
/* p */ (char) am_pm,
/* q */ (char) hour24,
/* r */ (char) invalid, /* short for "%I:%M:%S %p" */
/* s */ (char) invalid,
/* t */ (char) invalid, /* ignore tabs */
/* u */ (char) invalid,
/* v */ (char) largest_fast,
/* w */ (char) weekday_num,
/* x */ (char) invalid, /* short for "%m/%d/%y" */
/* y */ (char) year100,
/* z */ (char) largest_mem,
};
static char hms_fmt[] = "%H:%M:%S";
/* cases where a format letter is an alias for a longer format */
static char aliases_C[] = "%a %b %e %T %Z %Y";
static char aliases_D[] = "%m/%d/%y";
char *aliases_RX[7] = {
/* R */ "%H:%M",
/* S */ NULL,
/* T */ hms_fmt,
/* U */ NULL,
/* V */ NULL,
/* W */ NULL,
/* X */ hms_fmt
};
static char aliases_c[] = "%a %b %d %H:%M:%S %Y";
static char aliases_r[] = "%I:%M:%S %p";
static char aliases_x[] = "%m/%d/%y";
/* the (max) number of digits in each format kind. 0 means there's a
* special case for that kind.
*/
char field_digit_width[] = {
0, /* invalid */
0, /* space */
0, /* vbar */
0, /* string */
7, /* chip_mem */
4, /* chip_mem_K */
8, /* fast_mem */
5, /* fast_mem_K */
8, /* total_mem */
5, /* total_mem_K */
7, /* largest_chip */
4, /* largest_chip_K */
8, /* largest_fast */
5, /* largest_fast_K */
8, /* largest_mem */
5, /* largest_mem_K */
2, /* second */
2, /* minute */
2, /* hour12 */
2, /* hour12_0 */
2, /* hour24 */
2, /* hour24_0 */
0, /* am_pm */
3, /* julian */
2, /* day */
2, /* day0 */
2, /* week_num_sun */
2, /* week_num_mon */
0, /* weekday_abbrev */
0, /* weekday_full */
1, /* weekday_num */
2, /* monthnum0 */
2, /* monthnum */
0, /* month_abbrev */
0, /* month_full */
4, /* year */
2 /* year100 */
};
/* Which data must be inquired of the OS for each display_kind */
char field_requirements[] = {
NO_NEEDS_MASK, /* invalid */
NO_NEEDS_MASK, /* space */
NO_NEEDS_MASK, /* vbar */
NO_NEEDS_MASK, /* string */
NEED_CHIP_MASK, /* chip_mem */
NEED_CHIP_MASK, /* chip_mem_K */
NEED_FAST_MASK, /* fast_mem */
NEED_FAST_MASK, /* fast_mem_K */
NEED_MEM_MASK, /* total_mem */
NEED_MEM_MASK, /* total_mem_K */
NEED_BIG_CHIP_MASK, /* largest_chip */
NEED_BIG_CHIP_MASK, /* largest_chip_K */
NEED_BIG_FAST_MASK, /* largest_fast */
NEED_BIG_FAST_MASK, /* largest_fast_K */
NEED_BIG_MEM_MASK, /* largest_mem */
NEED_BIG_MEM_MASK, /* largest_mem_K */
NEED_TIME_MASK, /* second */
NEED_TIME_MASK, /* minute */
NEED_TIME_MASK, /* hour12 */
NEED_TIME_MASK, /* hour12_0 */
NEED_TIME_MASK, /* hour24 */
NEED_TIME_MASK, /* hour24_0 */
NEED_TIME_MASK, /* am_pm */
NEED_TIME_MASK, /* julian */
NEED_TIME_MASK, /* day */
NEED_TIME_MASK, /* day0 */
NEED_TIME_MASK, /* week_num_sun */
NEED_TIME_MASK, /* week_num_mon */
NEED_TIME_MASK, /* weekday_abbrev */
NEED_TIME_MASK, /* weekday_full */
NEED_TIME_MASK, /* weekday_num */
NEED_TIME_MASK, /* monthnum0 */
NEED_TIME_MASK, /* monthnum */
NEED_TIME_MASK, /* month_abbrev */
NEED_TIME_MASK, /* month_full */
NEED_TIME_MASK, /* year */
NEED_TIME_MASK /* year100 */
};
/*****************************************************************
Data Shared By Procedures In This File
*****************************************************************/
/*
* Some things that need to be shared with initialize() and done().
*/
static struct Window *Window = NULL;
static struct RastPort *win_rast;
static int win_baseline;
static int inleft, intop, inright, inbottom;
static struct timerequest delay_req;
static struct timerequest time_req;
static struct MsgPort *delay_port = NULL;
static struct MsgPort *time_port = NULL;
static struct Screen screen_info;
static struct Screen *my_screen;
static struct RastPort *scrn_rast;
static int digit_width; /* pixel width of a digit */
static int space_width; /* pixel width of space */
/* booleans indicating what info we need to query the OS for */
static int need_time=0, need_chip=0,
need_fast=0, need_big_chip=0, need_big_fast=0;
static int new_os; /* running OS >= 2.0? */
int offset_secs; /* computed from offset_minutes */
#ifdef CORRECTION
int correction = 1; /* secs to add every */
/* correction_interval */
#endif
static struct display_element *win_content; /* all display elements */
/* separate chains (though next_of_kind member) for memory and time displays */
static struct display_element *mem_chain = NULL;
static struct display_element *time_chain = NULL;
struct GfxBase *GfxBase;
struct IntuitionBase *IntuitionBase;
/*****************************************************************
Prototypes
*****************************************************************/
static void plan_display(char *format, int *pixelpos, int *pen,
struct display_element ***tailptr);
static void add_display_space(int width, int *pixelpos,
struct display_element ***tailptr);
static void add_display_string(char *string, int len, int *pixelpos, int *pen,
struct display_element ***tailptr);
static void add_display_format(char code, int *pixelpos, int *pen,
struct display_element ***tailptr);
static struct display_element *add_element(int width, int *pixelpos, int pen,
enum display_kind kind,
struct display_element ***tailptr);
static void insert_in_kind(struct display_element *elt,
struct display_element **chainptr);
static int widest(char **ptr, int count, int charcount);
static void replan_display(int curr_day, int curr_wkday, int curr_month);
static void display_strings(void);
static void draw_bar(long x);
static void display_data(void);
static void draw_datum(struct display_element *ptr, int val,
char **string_array, int len);
static char *int_string(int n, int *minwidth);
static void initialize(void);
static void done(int code);
/* this is defined in compute_date.c: */
void compute_date(long n, int *y, int *m, int *d, int *w, int *jul);
/*****************************************************************
Code
*****************************************************************/
void main(int argc, char *argv[]) {
int win_width;
struct Layer *MyLayer;
struct ClipRect *MyClip;
struct IntuiMessage *Msg;
struct Task *FindTask();
struct TextFont *font;
unsigned long timersig, windowsig, sigmask, sig;
unsigned long interval_micros;
int pen;
struct display_element **tail = &win_content;
/* set up */
initialize();
handle_args(argc, argv, arg_desc);
offset_secs = 60 * offset_minutes;
pen = (background_pen==1 ? 0 : 1);
inleft = (new_os ? WIDTH_OF_20_CLOSE_GADGET : WIDTH_OF_13_CLOSE_GADGET);
win_width = inleft + horiz_padding;
plan_display(format_string, &win_width, &pen, &tail);
win_width += horiz_padding;
New_Window.LeftEdge = (win_left<0 ?
screen_info.Width-win_width+win_left :
win_left);
New_Window.Width = win_width;
New_Window.Height = screen_info.Font->ta_YSize
+ screen_info.WBorTop + 1;
New_Window.TopEdge = (win_top<0 ?
screen_info.Height-New_Window.Height+win_top :
win_top);
Window = (struct Window *) OpenWindow(&New_Window);
if (Window == NULL) done(20);
/* set screen title to version string without leading "$VER: " */
SetWindowTitles(Window, NULL, (UBYTE*) &version[5]);
/* We've opened the window, now store away some info about it */
win_rast = Window->RPort;
my_screen = Window->WScreen;
font = OpenFont(screen_info.Font);
win_baseline = font->tf_Baseline+screen_info.WBorTop;
SetAPen(win_rast, 1L);
SetBPen(win_rast, background_pen);
SetDrMd(win_rast, JAM2);
SetFont(win_rast, font);
intop = screen_info.WBorTop - 1;
inright = win_width - 2;
inbottom = New_Window.Height - 2;
MyLayer = win_rast->Layer;
timersig = 1L << delay_port->mp_SigBit;
windowsig = 1L << Window->UserPort->mp_SigBit;
sigmask = timersig | windowsig | SIGBREAKF_CTRL_C;
interval_micros = interval_tenth_secs*100000;
{
int yr, month, dy, weekday, jul;
/* display clock the first time. First get the current time */
DoIO((struct IORequest *) &time_req.tr_node);
compute_date((time_req.tr_time.tv_secs+offset_secs)/SECS_PER_DAY,
&yr, &month, &dy, &weekday, &jul);
replan_display(dy, weekday, month);
display_strings();
display_data();
}
display_strings();
display_data();
(void) SetTaskPri(FindTask(NULL), priority);
#ifdef CORRECTION
if (correction_ratio != 0) {
if (correction_ratio < 0) {
correction = -1;
correction_ratio = -correction_ratio;
}
need_time = 1;
}
#endif
for (;;) {
int strings_need_display = 0;
sig = Wait(sigmask);
if (sig & windowsig) {
while (Msg=(struct IntuiMessage *)GetMsg(Window->UserPort)) {
switch (Msg->Class) {
case IDCMP_CLOSEWINDOW:
ReplyMsg((struct Message *)Msg);
done(0);
break;
default:
strings_need_display = 1;
break;
}
ReplyMsg((struct Message *)Msg);
}
}
if ((sig & timersig) && GetMsg(delay_port)) {
delay_req.tr_time.tv_secs = 0;
delay_req.tr_time.tv_micro = interval_micros;
SendIO((struct IORequest *) &delay_req.tr_node);
}
if (sig & SIGBREAKF_CTRL_C) done(20);
/* no point doing anything if our screen is not in front */
if (my_screen != IntuitionBase->FirstScreen) continue;
/* check if somone is on top of us */
MyClip = MyLayer->ClipRect;
if (MyClip && (MyClip->Next || MyClip->lobs)) {
WindowToFront(Window); /* keep us on top */
strings_need_display = 1;
}
if (strings_need_display) display_strings();
display_data();
}
}
/* Create a chain of display_elements describing the clock display,
* and return the interior width of the clock window. format is the
* format string. *pixelpos is the position at which to start the
* next part of the display; on completion, it is the width of the
* (inside of) the window. Pen is the pen color in which to draw the
* next part of the display. tailptr points to a pointer to a (NULL)
* pointer to the next display element, allowing us to keep adding to
* the end of the chain. On return, *tailptr will point to the new
* NULL next pointer.
*/
static void plan_display(char *format, int *pixelpos, int *pen,
struct display_element ***tailptr)
{
char *ptr = format;
do {
if (*ptr==' ') {
int width = 0;
do {width += space_width;} while (*++ptr==' ');
add_display_space(width, pixelpos, tailptr);
} else if (*ptr == '%') {
add_display_format(*++ptr, pixelpos, pen, tailptr);
++ptr;
} else if (*ptr != '\0') {
char *strstart = ptr;
do {++ptr;} while (*ptr!='%' && *ptr!='\0');
/* we'll handle spaces in next iteration, so trim them */
while (*--ptr == ' '); /* trim trailing blanks */
++ptr; /* point after string */
add_display_string(strstart, ptr-strstart, pixelpos, pen,
tailptr);
}
} while (*ptr != '\0');
}
static void add_display_space(int width, int *pixelpos,
struct display_element ***tailptr)
{
add_element(width, pixelpos, 0, space, tailptr);
}
/* create a display_element for the string starting at string and
* whose length is len. Add the width of this string to pixelpos.
*/
static void add_display_string(char *str, int len, int *pixelpos, int *pen,
struct display_element ***tailptr)
{
struct display_element *elt =
add_element(TextLength(scrn_rast,str,len), pixelpos, *pen, string,
tailptr);
elt->string.text = str;
elt->string.length = len;
}
/* create a new display_element record or records for format character
* code. I.e., we've seen a '%' followed by code, and now want to
* handle it.
*/
static void add_display_format(char code, int *pixelpos, int *pen,
struct display_element ***tailptr)
{
struct display_element *elt;
enum display_kind kind = invalid;
char *alias = NULL;
int bits;
int width;
if (code >= 'A' && code <= 'Z') {
if (code == 'C') alias = aliases_C;
else if (code == 'D') alias = aliases_D;
else if (code >= 'R' && code <= 'X') alias = aliases_RX[code-'R'];
if (alias != NULL) {
plan_display(alias, pixelpos, pen, tailptr);
return;
} else {
kind = upper_codes[code-'A'];
}
} else if (code >= 'a' && code <= 'z') {
if (code == 'c') alias = aliases_c;
else if (code == 'r') alias = aliases_r;
else if (code == 'x') alias = aliases_x;
if (alias != NULL) {
plan_display(alias, pixelpos, pen, tailptr);
return;
} else {
kind = lower_codes[code-'a'];
}
} else if (code >= '0' && code <= '9') {
*pen = code-'0';
return;
} else if (code == '|') {
kind = vbar;
} else if (code == '%') {
add_display_string("%", 1, pixelpos, pen, tailptr);
return;
}
switch (kind) {
case weekday_abbrev: width = widest(str_weekday, 7, 3); break;
case weekday_full: width = widest(str_weekday, 7, 0); break;
case month_abbrev: width = widest(str_month, 12, 3); break;
case month_full: width = widest(str_month, 12, 0); break;
case am_pm: width = widest(str_am_pm, 2, 0); break;
case vbar: width = 2*horiz_padding + 1 + (new_os!=0); break;
default: width = digit_width *
field_digit_width[(int)kind]; break;
}
elt = add_element(width, pixelpos, *pen, kind, tailptr);
elt->data.curr_value = -1; /* universal bogus value */
bits = field_requirements[(int)kind];
if (bits & NEED_CHIP_MASK) need_chip = 1;
if (bits & NEED_FAST_MASK) need_fast = 1;
if (bits & NEED_BIG_CHIP_MASK) need_big_chip = 1;
if (bits & NEED_BIG_FAST_MASK) need_big_fast = 1;
if (bits & NEED_TIME_MASK) {
need_time = 1;
insert_in_kind(elt, &time_chain);
} else if (bits != NO_NEEDS_MASK) {
/* must have been one of the memory items */
insert_in_kind(elt, &mem_chain);
}
}
/* Create a new generic display_element, fill it in and return it. */
static struct display_element *add_element(int width, int *pixelpos, int pen,
enum display_kind kind,
struct display_element ***tailptr)
{
struct display_element *new = AllocMem(sizeof(struct display_element),
MEMF_ANY);
new->next = NULL;
new->next_of_kind = NULL;
new->left = *pixelpos;
new->max_width = width;
new->curr_width = width;
*pixelpos += width;
new->pen = pen;
new->kind = kind;
**tailptr = new;
*tailptr = &(new->next);
return new;
}
/* Insert a display_element into an existing chain (through the
* next_of_kind field) in sorted order.
*/
static void insert_in_kind(struct display_element *elt,
struct display_element **chainptr)
{
enum display_kind kind = elt->kind;
struct display_element *ptr = *chainptr;
while (ptr!=NULL && ptr->kind<kind) {
chainptr = &(ptr->next_of_kind);
ptr = *chainptr;
}
elt->next_of_kind = ptr;
*chainptr = elt;
}
/* Return the width of the widest of the count strings in the array
* ptr of string pointers. If charcount is >0, then consider only the
* first charcount characters of each string.
*/
static int widest(char **ptr, int count, int charcount)
{
int max = 0;
int width;
for (; count>0; --count, ++ptr) {
width = TextLength(scrn_rast, *ptr, (charcount!=0 ? charcount :
strlen(*ptr)));
if (width > max) max = width;
}
return max;
}
/* Walk down the full display list recentering data in their part of
* the display. A part of the display is delimited by vertical bars
* (or the ends of the window). For each part, we traverse it once,
* adding up the actual sizes of everything, and then traverse it
* again placing everything where it should go.
*/
static void replan_display(int curr_day, int curr_wkday, int curr_month)
{
struct display_element *ptr = win_content;
struct display_element *this_part;
int left = inleft + horiz_padding;
while(ptr != NULL) {
int changed = 0; /* has this part changed? */
int total_width = 0; /* width of this part */
int total_max_width = 0; /* max width of this part */
this_part = ptr;
for (; ptr!=NULL && ptr->kind!=vbar; ptr=ptr->next) {
int width;
switch (ptr->kind) {
case day:
/* remember: day is 0 origin, so '9' is the 10th */
width = digit_width * (curr_day<9 ? 1 : 2);
break;
case weekday_abbrev:
width = TextLength(Window->RPort,str_weekday[curr_wkday],3);
break;
case weekday_full:
width = TextLength(Window->RPort,str_weekday[curr_wkday],
strlen(str_weekday[curr_wkday]));
break;
case month_abbrev:
width = TextLength(Window->RPort,str_month[curr_month],3);
break;
case month_full:
width = TextLength(Window->RPort,str_month[curr_month],
strlen(str_month[curr_month]));
break;
default:
width = ptr->max_width; /* width never changes */
break;
}
if (width != ptr->curr_width) {
ptr->curr_width = width;
changed = 1;
}
total_width += width;
total_max_width += ptr->max_width;
}
if (changed) {
left += (total_max_width-total_width)/2;
for (ptr=this_part;
ptr!=NULL && ptr->kind!=vbar;
ptr=ptr->next) {
ptr->left = left;
left += ptr->curr_width;
}
}
if (ptr != NULL) {
left = ptr->left + ptr->max_width;
ptr = ptr->next;
}
}
}
/* Clear the window and display all strings and bars. Also
* invalidate all previous values for all update records, since after
* this no values will be up-to-date.
*/
static void display_strings(void)
{
struct display_element *ptr;
SetAPen(win_rast, background_pen);
RectFill(win_rast, inleft, intop, inright, inbottom);
for (ptr=win_content; ptr!=NULL; ptr=ptr->next) {
switch(ptr->kind) {
case invalid: case space:
break; /* nothing to do */
case vbar:
draw_bar(ptr->left+horiz_padding);
break;
case string:
Move(Window->RPort, ptr->left, win_baseline);
SetAPen(win_rast, ptr->pen);
Text(Window->RPort, ptr->string.text, ptr->string.length);
break;
default:
ptr->data.curr_value = -1; /* force later redisplay */
}
}
}
/* draw a top-to-bottom bar in window at position x */
static void draw_bar(long x)
{
long bottom = Window->BorderTop-2;
if (new_os) {
/* a nice 3d bar */
SetAPen(win_rast, 1L);
Move(Window->RPort, x, 1L);
Draw(Window->RPort, x, bottom);
SetAPen(win_rast, 2L);
Move(Window->RPort, x+1, 1L);
Draw(Window->RPort, x+1, bottom);
} else {
/* boring old single line for pre-2.0 systems*/
SetAPen(win_rast, 1L);
Move(Window->RPort, x+1, 1L);
Draw(Window->RPort, x+1, bottom);
}
}
/* display all data in clock that have changed since last display. */
static void display_data(void)
{
struct display_element *ptr;
unsigned long secs;
int chip_free, fast_free, big_chip_free, big_fast_free;
/* yr < 0 means we haven't computed yr, month, dy, weekday yet */
int yr=-1, month, dy, weekday, jul;
if (need_time) {
#ifdef CORRECTION
static unsigned long next_correction = 0;
#endif
DoIO((struct IORequest *) &time_req.tr_node);
secs = time_req.tr_time.tv_secs + offset_secs;
#ifdef CORRECTION
if (secs >= next_correction) {
if (next_correction == 0) {
/* need to set up next_correction */
next_correction = (correction_ratio==0 ?
END_OF_TIME :
secs + correction_ratio);
} else {
/* really need to make a one second correction now */
time_req.tr_node.io_Command = TR_SETSYSTIME;
time_req.tr_time.tv_secs += correction;
DoIO((struct IORequest *) &time_req.tr_node);
time_req.tr_node.io_Command = TR_GETSYSTIME;
next_correction += correction_ratio + correction;
}
}
#endif
for (ptr=time_chain; ptr!=NULL; ptr=ptr->next_of_kind) {
int is_date = 0;
int modulus;
unsigned long val; /* the numeric value to show */
int len = 0; /* minimum int width, max string
* width (0 means unlimited)
*/
char **string_array = NULL; /* the array whose val-th elt
* to show, or NULL, to show
* val as a number
*/
switch (ptr->kind) {
case hour24: val=secs/3600; modulus=24; break;
case hour12: val=secs/3600; modulus=12; break;
case hour24_0: val=secs/3600; modulus=24; len=2; break;
case hour12_0: val=secs/3600; modulus=12; len=2; break;
case minute: val=secs/60; modulus=60; len=2; break;
case second: val=secs; modulus=60; len=2; break;
case am_pm: val=secs/43200;modulus= 2;
string_array=str_am_pm; break;
default : val=secs/SECS_PER_DAY; is_date=1; break;
}
/* chain is sorted, so when we get to an element that hasn't
* changed, we know nothing later will have changed.
*/
if (val == ptr->data.curr_value) break;
if (is_date) {
if (yr < 0) {
/* we haven't figured out the date yet, so do it now. */
compute_date(val, &yr, &month, &dy, &weekday, &jul);
if (ptr->data.curr_value != -1) {
replan_display(dy, weekday, month);
display_strings();
display_data();
return;
}
}
ptr->data.curr_value = val;
switch (ptr->kind) {
case year: val=yr; break;
case year100: val=yr%100; len=2; break;
case monthnum0: val=month+1; len=2; break;
case monthnum: val=month+1; break;
case month_abbrev: val=month; len=3;
string_array=str_month; break;
case month_full: val=month;
string_array=str_month; break;
case week_num_sun: val=(14+jul-((7+jul-weekday)%7))/7;
break;
case week_num_mon: val=(14+jul-((8+jul-weekday)%7))/7;
break;
case weekday_abbrev: val=weekday; len=3;
string_array=str_weekday; break;
case weekday_full: val=weekday;
string_array=str_weekday; break;
case weekday_num: val=1+weekday; break;
case day0: val=dy+1; len=2; break;
case day: val=dy+1; break;
case julian: val=jul+1; break;
}
} else {
val %= modulus;
ptr->data.curr_value = val;
}
draw_datum(ptr, val, string_array, len);
}
}
if (need_chip) chip_free = AvailMem(MEMF_CHIP);
if (need_fast) fast_free = AvailMem(MEMF_FAST);
if (need_big_chip) big_chip_free = AvailMem(MEMF_CHIP|MEMF_LARGEST);
if (need_big_fast) big_fast_free = AvailMem(MEMF_FAST|MEMF_LARGEST);
for (ptr=mem_chain; ptr!=NULL; ptr=ptr->next_of_kind) {
int val; /* the numeric value to show */
switch (ptr->kind) {
case chip_mem: val=chip_free; break;
case chip_mem_K: val=(chip_free+512)>>10; break;
case fast_mem: val=fast_free; break;
case fast_mem_K: val=(fast_free+512)>>10; break;
case total_mem: val=chip_free+fast_free; break;
case total_mem_K: val=(chip_free+fast_free+512)>>10; break;
case largest_chip: val=big_chip_free; break;
case largest_chip_K: val=(big_chip_free+512)>>10; break;
case largest_fast: val=big_fast_free; break;
case largest_fast_K: val=(big_fast_free+512)>>10; break;
case largest_mem: val=big_chip_free+big_fast_free; break;
case largest_mem_K: val=(big_chip_free+big_fast_free+512)>>10;
break;
}
draw_datum(ptr, val, NULL, 0);
}
}
/* display a single datum in the window. *ptr describes where the
* datum should be displayed, val is the value to display. If
* string_array is non-NULL, then show its val-th element as a string,
* rather than showing a number. If len is not 0, show only the first
* len characters of the specified string, or show exactly len digits,
* 0-filled if necessary, of val if string_array is NULL.
*/
static void draw_datum(struct display_element *ptr, int val,
char **string_array, int len)
{
char *todraw;
int pixelleft;
if (string_array != NULL) {
todraw = string_array[val];
if (len==0) len=strlen(todraw);
} else {
todraw = int_string(val, &len);
}
pixelleft = ptr->left + ptr->curr_width -
TextLength(Window->RPort,todraw,len);
if (ptr->left < pixelleft) {
SetAPen(win_rast, background_pen);
RectFill(win_rast, ptr->left, intop, pixelleft-1, inbottom);
}
Move(Window->RPort, pixelleft, win_baseline);
SetAPen(win_rast, ptr->pen);
Text(Window->RPort, todraw, len);
}
/* cheap int->string conversion. Doesn't handle negative numbers, and
* always fills to the specified minimum width with 0's. It returns a
* pointer to a static buffer, so one call's value is destroyed by the
* next call to this function. But that's all we need for this clock.
* Note that on completion we set *minwidth to the actual width.
*/
#define INT_STRING_BUFF_LEN 8
static char *int_string(int n, int *minwidth)
{
static char buffer[INT_STRING_BUFF_LEN];
char *ptr = &buffer[INT_STRING_BUFF_LEN-1];
int len=0, mw=*minwidth;
*ptr = '\0';
do {
*--ptr = '0'+n%10;
n/=10;
} while ((++len)<mw || n > 0);
*minwidth = len;
return ptr;
}
/* open libraries and initialize the timer device. */
static void initialize(void)
{
if ((IntuitionBase = (struct IntuitionBase *)
OpenLibrary("intuition.library", INTUITION_REV)) == NULL)
done(20);
if ((GfxBase = (struct GfxBase *)
OpenLibrary("graphics.library", 0L)) == NULL)
done(20);
/* set up delay clock, which will wake us every second */
if ((delay_port = CreatePort(NULL, 0L)) == NULL)
done(20);
if (OpenDevice(TIMERNAME, UNIT_VBLANK,
(struct IORequest *) &delay_req, 0L) != NULL)
done(20);
delay_req.tr_node.io_Message.mn_ReplyPort = delay_port;
delay_req.tr_node.io_Command = TR_ADDREQUEST;
delay_req.tr_node.io_Flags = 0;
delay_req.tr_node.io_Error = 0;
delay_req.tr_time.tv_secs = 0;
delay_req.tr_time.tv_micro = 1000000;
/* send first request, to get us started */
SendIO((struct IORequest *) &delay_req.tr_node);
/* set up time clock, which will get us system time */
if ((time_port = CreatePort(NULL, 0L)) == NULL)
done(20);
if (OpenDevice(TIMERNAME, UNIT_VBLANK /* does this matter? */,
(struct IORequest *) &time_req, 0L) != NULL)
done(20);
time_req.tr_node.io_Message.mn_ReplyPort = time_port;
time_req.tr_node.io_Command = TR_GETSYSTIME;
time_req.tr_node.io_Flags = 0;
time_req.tr_node.io_Error = 0;
if (!GetScreenData(&screen_info, sizeof(screen_info), WBENCHSCREEN,
NULL))
done(20);
scrn_rast = &screen_info.RastPort;
digit_width = TextLength(scrn_rast, "9", 1);
space_width = TextLength(scrn_rast, " ", 1);
new_os = ((struct Library *)IntuitionBase)->lib_Version >= 37;
/* now we can set the default window left */
win_left = -2*(new_os ? WIDTH_OF_20_DEPTH_GADGET
: WIDTH_OF_13_DEPTH_GADGET);
}
/* just clean up that which is open, and then leave. */
static void done(int code)
{
handle_args_finish(); /* cleanup arg handling */
if (Window) CloseWindow(Window);
if (delay_req.tr_node.io_Message.mn_ReplyPort) {
AbortIO((struct IORequest *) &delay_req.tr_node);
CloseDevice((struct IORequest *) &delay_req);
}
if (delay_port) DeletePort(delay_port);
if (time_req.tr_node.io_Message.mn_ReplyPort)
CloseDevice((struct IORequest *) &time_req);
if (time_port) DeletePort(time_port);
if (IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
if (GfxBase) CloseLibrary((struct Library *)GfxBase);
/* free allocated memory */
while (win_content != NULL) {
struct display_element *next = win_content->next;
FreeMem(win_content, sizeof(*win_content));
win_content = next;
}
/* __exit() doesn't try to close files, so it makes a much
* smaller executable than using exit(). This is the correct
* thing to do, since we compile with the nostdio option.
*/
__exit((long)code);
}